package org.example.redisson;

import cn.hutool.core.lang.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.TimeUnit;

/**
 * @Classname RedisLockImpl
 * @Description 封装redis分布锁加锁解锁
 * @Date 2021/3/24 4:16 下午
 * <p>
 * 这样做会导致阻塞
 * @Created by xiangjd
 */
public class RedisLockImpl4 implements RedisLock {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //把加锁和解锁放在同一个线程里,防止不同线程之间串改
    private ThreadLocal<String> threadLocal = new ThreadLocal<String>();

    private ThreadLocal<Integer> threadLocalInteger = new ThreadLocal<Integer>();

    @Override
    public boolean tryLock(String key, long timeout, TimeUnit unit) {
        boolean isLock = false;
        if (threadLocal.get() == null) {
            String uuid = UUID.randomUUID().toString();
            threadLocal.set(uuid);
            isLock = stringRedisTemplate.opsForValue().setIfAbsent(key, uuid, timeout, unit);
            //如果获取锁失败，则自旋获取锁，直到成功
            if (!isLock) {
                for (; ; ) {
                    isLock = stringRedisTemplate.opsForValue().setIfAbsent(key, uuid, timeout, unit);
                    if (isLock) {
                        break;
                    }
                }
            }

            //定义更新锁的时间
            while (true) {
                Integer count = threadLocalInteger.get();
                if (count == null || count <= 0) {
                    break;
                }
                stringRedisTemplate.expire(key, 30, TimeUnit.SECONDS);
                try {
                    //每隔10秒执行1次
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        } else {
            isLock = true;
        }
        if (isLock) {
            Integer count = threadLocalInteger.get() == null ? 0 : threadLocalInteger.get();
            threadLocalInteger.set(count++);
        }
        return isLock;
    }

    @Override
    public void releaseLock(String key) {
        //当前线程的uuid和redis中的uuid一样才能进行删除
        if (threadLocal.get().equals(stringRedisTemplate.opsForValue().get(key))) {
            Integer count = threadLocalInteger.get();
            //计数器减为0时释放锁
            if (count == 0 || --count <= 0) {
                stringRedisTemplate.delete(key);
            }

        }
    }
}
